﻿using System;
using System.Drawing;
using System.Windows.Forms;
using SpriteAnimator.Classes;
using System.Collections.Generic;

namespace SpriteAnimator
{
    /// <summary>
    /// Permet de gérer les frames d'un sprite
    /// </summary>
    public partial class frmFrames : Form
    {
        #region Variables d'instances

        private Sprite m_oSprite;

        #endregion

        #region Constructeurs

        /// <summary>
        /// Constructeur de le fenêtre
        /// </summary>
        /// <param name="p_oSprite">Instance du sprite à gérer</param>
        public frmFrames(Sprite p_oSprite)
        {
            this.InitializeComponent();

            // *** Initialisation ***

            this.Frames = null;
            this.m_oSprite = p_oSprite;
            this.picPreview.BackColor = p_oSprite.BackColor;
            this.chkCadre.Checked = Utils.GetRegistryValue("SpriteRectangle", "1", Utils.REGISTRY_KEY).Equals("1");
            this.chkPreview.Checked = Utils.GetRegistryValue("SpritePreview", "1", Utils.REGISTRY_KEY).Equals("1");

            this.ShowPreview(this.chkPreview.Checked);

            // *** Info texture ***

            this.lblInformation.Text = String.Format("Information texture : {0} x {1}", this.m_oSprite.Texture.Width, this.m_oSprite.Texture.Height);

            // *** Binding des données sur l'interface ***

            this.GenerateFrames();
        }

        #endregion

        #region Evénéments de l'application

        /// <summary>
        /// Active ou non le cadre de rendu
        /// </summary>
        /// <param name="sender">Source de l'appel</param>
        /// <param name="e">Argument de l'appel</param>
        private void chkCadre_CheckedChanged(object sender, EventArgs e)
        {
            this.PreviewFrame();
        }

        /// <summary>
        /// Change la visibilité de la zone de preview
        /// </summary>
        /// <param name="sender">Source de l'appel</param>
        /// <param name="e">Argument de l'appel</param>
        private void chkPreview_CheckedChanged(object sender, EventArgs e)
        {
            this.ShowPreview(this.chkPreview.Checked);
        }

        /// <summary>
        /// Se produit sur l'ajoute d'une frame
        /// </summary>
        /// <param name="sender">Source de l'appel</param>
        /// <param name="e">Argument de l'appel</param>
        private void cmdAdd_Click(object sender, EventArgs e)
        {
            // *** Ouverture de la frame de sélection

            using ( frmFrameEdit oEdit = new frmFrameEdit(null, this.m_oSprite.Texture, this.m_oSprite.BackColor) )
            {
                if ( oEdit.ShowDialog(this) == DialogResult.OK )
                {
                    // *** Ajout à la liste ***

                    ListViewItem oItem = new ListViewItem();
                    oItem.Tag = oEdit.Frame;
                    oItem.Text = string.Empty;
                    oItem.SubItems.Add(oEdit.Frame.X.ToString());
                    oItem.SubItems.Add(oEdit.Frame.Y.ToString());
                    oItem.SubItems.Add(oEdit.Frame.Width.ToString());
                    oItem.SubItems.Add(oEdit.Frame.Height.ToString());

                    this.lstFrames.Items.Add(oItem);
                }
            }

            // *** On numérote ***

            this.Renumeroter();
        }

        /// <summary>
        /// Se produit sur la suppression d'une frame
        /// </summary>
        /// <param name="sender">Source de l'appel</param>
        /// <param name="e">Argument de l'appel</param>
        private void cmdRemove_Click(object sender, EventArgs e)
        {
            // *** Vérification ***

            if ( this.lstFrames.SelectedIndices.Count < 1 )
            {
                MessageBox.Show(this, "Vous devez sélectionner une frame.", "Suppression", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }

            // *** Demande de confirmation ***

            if ( MessageBox.Show(this, "Etes-vous sûr de vouloir supprimer la frame " + (this.lstFrames.SelectedIndices[0] + 1).ToString() + " ?", "Suppression", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No )
                return;

            // *** Suppression de la frame ***

            this.lstFrames.Items.RemoveAt(this.lstFrames.SelectedIndices[0]);

            // *** On rafraichis tout ****

            this.Renumeroter();
        }

        /// <summary>
        /// Annule la saisie
        /// </summary>
        /// <param name="sender">Source de l'appel</param>
        /// <param name="e">Argument de l'appel</param>
        private void cmdClose_Click(object sender, EventArgs e)
        {
            this.DialogResult = DialogResult.Cancel;
            this.Close();
        }

        /// <summary>
        /// Valide la saisie
        /// </summary>
        /// <param name="sender">Source de l'appel</param>
        /// <param name="e">Argument de l'appel</param>
        private void cmdValider_Click(object sender, EventArgs e)
        {
            // *** Génération de la liste des frames ***

            this.GenerateRectangle();

            // *** On ferme la fenêtre ***

            this.DialogResult = DialogResult.OK;
            this.Close();
        }

        /// <summary>
        /// Déplace une frame vers le haut
        /// </summary>
        /// <param name="sender">Source de l'appel</param>
        /// <param name="e">Argument de l'appel</param>
        private void cmdUp_Click(object sender, EventArgs e)
        {
            // *** Vérification ***

            if ( this.lstFrames.SelectedIndices.Count < 1 )
            {
                MessageBox.Show(this, "Vous devez sélectionner une frame.", "Mouvement", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }

            int iIndex = this.lstFrames.SelectedIndices[0];

            if ( iIndex < 1 )
                return;

            // *** Inversion ***

            ListViewItem oItem = this.lstFrames.Items[iIndex];

            this.lstFrames.Items.RemoveAt(iIndex);
            this.lstFrames.Items.Insert(iIndex - 1, oItem);

            this.Renumeroter();
        }

        /// <summary>
        /// Déplace une frame vers le bas
        /// </summary>
        /// <param name="sender">Source de l'appel</param>
        /// <param name="e">Argument de l'appel</param>
        private void cmdDown_Click(object sender, EventArgs e)
        {
            // *** Vérification ***

            if ( this.lstFrames.SelectedIndices.Count < 1 )
            {
                MessageBox.Show(this, "Vous devez sélectionner une frame.", "Mouvement", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }

            int iIndex = this.lstFrames.SelectedIndices[0];

            if ( iIndex > (this.lstFrames.Items.Count - 2) )
                return;

            // *** Inversion ***

            ListViewItem oItem = this.lstFrames.Items[iIndex];

            this.lstFrames.Items.RemoveAt(iIndex);
            this.lstFrames.Items.Insert(iIndex + 1, oItem);

            this.Renumeroter();
        }

        /// <summary>
        /// Recharge la liste des frames
        /// </summary>
        /// <param name="sender">Source de l'appel</param>
        /// <param name="e">Argument de l'appel</param>
        private void cmdReload_Click(object sender, EventArgs e)
        {
            // *** Confirmation ***

            if ( MessageBox.Show(this, "Cette action va réinitialiser les frames comme à l'ouverture de la fenêtre.\nCela effacera tout ce qui a été fait, voulez-vous continuer ?", "Avertissement", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No )
                return;

            // *** Réninitialisation ***

            this.GenerateFrames();
        }

        /// <summary>
        /// Modifie une frame existante
        /// </summary>
        /// <param name="sender">Source de l'appel</param>
        /// <param name="e">Argument de l'appel</param>
        private void lstFrames_DoubleClick(object sender, EventArgs e)
        {
            // *** Vérification ***

            if ( this.lstFrames.SelectedIndices.Count < 1 )
                return;

            ListViewItem oItem = this.lstFrames.Items[this.lstFrames.SelectedIndices[0]];

            // *** Ouverture de la frame de sélection

            using ( frmFrameEdit oEdit = new frmFrameEdit((Rectangle)oItem.Tag, this.m_oSprite.Texture, this.m_oSprite.BackColor) )
            {
                if ( oEdit.ShowDialog(this) == DialogResult.OK )
                {
                    // *** Ajout à la liste ***

                    oItem.Tag = oEdit.Frame;
                    oItem.SubItems[1].Text = oEdit.Frame.X.ToString();
                    oItem.SubItems[2].Text = oEdit.Frame.Y.ToString();
                    oItem.SubItems[3].Text = oEdit.Frame.Width.ToString();
                    oItem.SubItems[4].Text = oEdit.Frame.Height.ToString();
                }
            }

            // *** Numérotation ***

            this.Renumeroter();
        }

        /// <summary>
        /// Au changement de frame
        /// </summary>
        /// <param name="sender">Source de l'appel</param>
        /// <param name="e">Argument de l'appel</param>
        private void lstFrames_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.PreviewFrame();
        }

        #endregion

        #region Fonctions privées

        /// <summary>
        /// Génère les frames pour l'image list
        /// </summary>
        private void GenerateFrames()
        {
            // *** Initialisation ***

            this.lstFrames.Items.Clear();

            // *** Création des images ***

            foreach ( Rectangle oRect in this.m_oSprite.Frames )
            {
                // *** Ajout à la liste ***

                ListViewItem oItem = new ListViewItem();
                oItem.Tag = oRect;
                oItem.Text = string.Empty;
                oItem.SubItems.Add(oRect.X.ToString());
                oItem.SubItems.Add(oRect.Y.ToString());
                oItem.SubItems.Add(oRect.Width.ToString());
                oItem.SubItems.Add(oRect.Height.ToString());

                this.lstFrames.Items.Add(oItem);
            }

            // *** Numérotation ***

            this.Renumeroter();

            // *** Sélection du premier ***

            if ( this.lstFrames.Items.Count > 0 )
                this.lstFrames.Items[0].Selected = true;
        }

        /// <summary>
        /// Génère la liste des frames sous forme de rectangle
        /// </summary>
        private void GenerateRectangle()
        {
            this.Frames = new List<Rectangle>();

            // *** On parcours les items ***

            foreach ( ListViewItem oItem in this.lstFrames.Items )
                this.Frames.Add((Rectangle)oItem.Tag);
        }

        /// <summary>
        /// Lance la prévisualisation de la frame
        /// </summary>
        private void PreviewFrame()
        {
            // *** Libération de l'ancienne image ***

            if ( this.picPreview.Image != null )
            {
                this.picPreview.Image.Dispose();
                this.picPreview.Image = null;
            }

            // *** Vérification sélection ***

            if ( this.lstFrames.SelectedIndices.Count > 0 )
            {
                // *** Récupération rectangle de la frame ***

                Rectangle oRect = (Rectangle)this.lstFrames.Items[this.lstFrames.SelectedIndices[0]].Tag;

                // *** Génération de l'image ***

                Bitmap oImg = new Bitmap(oRect.Width, oRect.Height);

                // *** On récupère la zone de dessin ***

                using ( Graphics oGraphics = Graphics.FromImage(oImg) )
                {
                    // *** Ecriture de l'image ***

                    oGraphics.DrawImage(this.m_oSprite.Texture, new Rectangle(0, 0, oRect.Width, oRect.Height), oRect, GraphicsUnit.Pixel);

                    // *** Cadre ***

                    if ( this.chkCadre.Checked )
                    {
                        using ( Pen oPen = new Pen(Color.Black) )
                        {
                            oGraphics.DrawRectangle(oPen, new Rectangle(0, 0, oRect.Width - 1, oRect.Height - 1));
                        }
                    }
                }

                // *** Ajout au contrôle ***

                this.picPreview.Image = oImg;
            }

            // *** Ré affichage ***

            this.picPreview.Invalidate();
        }

        /// <summary>
        /// Renumérote les frames
        /// </summary>
        private void Renumeroter()
        {
            int iCount = 1;

            foreach ( ListViewItem oItem in this.lstFrames.Items )
            {
                oItem.Text = iCount.ToString();
                iCount++;
            }
        }

        /// <summary>
        /// Affiche ou masque la zone de preview
        /// </summary>
        /// <param name="p_bView">TRUE affiche la zone de preview, FALSE sinon</param>
        private void ShowPreview(bool p_bView)
        {
            if ( p_bView )
                this.Width = this.picPreview.Left + this.picPreview.Width + 15;
            else
                this.Width = this.lstFrames.Left + this.lstFrames.Width + 15;

            this.picPreview.Visible = p_bView;
            this.chkCadre.Visible = p_bView;
        }

        #endregion

        #region Indexer

        /// <summary>
        /// Retournes les frames
        /// </summary>
        public List<Rectangle> Frames
        {
            get;
            private set;
        }

        #endregion
    }
}
